// 定义包名
package com.heima.search.service.impl;

// 引入 Hutool 工具类库，用于集合操作和字符串处理等
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollStreamUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
// 引入 Elasticsearch 客户端库相关类
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.SortOrder;
import co.elastic.clients.elasticsearch._types.query_dsl.BoolQuery;
import co.elastic.clients.elasticsearch._types.query_dsl.Query;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.json.JsonData;
// 引入自定义的响应结果类和 DTO 类
import com.heima.common.dtos.ResponseResult;
import com.heima.model.search.dtos.UserSearchDto;
import com.heima.model.search.vos.SearchArticleVo;
// 引入自定义的搜索服务接口
import com.heima.search.dto.ApAssociateWordsDto;
import com.heima.search.pojos.ApAssociateWords;
import com.heima.search.pojos.Suggestion;
import com.heima.search.service.ArticleSearchService;
// 使用 lombok 库的 @Slf4j 注解自动为类生成日志对象
import lombok.extern.slf4j.Slf4j;
// 标识这个类是一个 Spring 服务组件
import org.springframework.stereotype.Service;

// 通过 @Resource 注解自动注入 Elasticsearch 客户端
import javax.annotation.Resource;
// 导入 IOException 用于处理可能发生的 IO 异常
import java.io.IOException;
// 导入 List 接口，用于定义方法返回类型
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

// 使用 @Slf4j 注解生成日志对象 log
@Slf4j
// 使用 @Service 注解标识这是一个服务组件
@Service
public class ArticleSearchServiceImpl implements ArticleSearchService {

    // 注入 Elasticsearch 客户端
    @Resource
    private ElasticsearchClient elasticsearchClient;
    // 定义索引名称常量
    private static final String INDEX_NAME = "app_info_article";

    /**
     * Elasticsearch 进行文章的分页检索。
     */
    @Override
    public ResponseResult<List<SearchArticleVo>> search(UserSearchDto dto) throws IOException {
        // 1. 构建基本的查询对象
        Query query = this.buildBasicQuery(dto);
        // 分页参数，从 dto 中获取每页大小和当前页码
        int size = dto.getPageSize();
        int from = dto.getFromIndex();

        // 2. 执行搜索操作，获取搜索响应
        SearchResponse<SearchArticleVo> searchResponse = this.elasticsearchClient.search(builder -> builder
                        // 指定索引名称
                        .index(INDEX_NAME)
                        // 设置查询条件
                        .query(query)
                        // 设置从第几个结果开始
                        .from(from)
                        // 设置每页显示多少条结果
                        .size(size)
                        // 设置按照发布时间倒序排序
                        .sort(s -> s.field(f -> f.field("publishTime").order(SortOrder.Desc)))
                        // 设置标题高亮显示
                        .highlight(h -> h.fields("title", f -> f
                                .requireFieldMatch(false)
                                .preTags("<font style='color: red; font-size: inherit;'>")
                                .postTags("</font>")))
                , SearchArticleVo.class);

        // 3. 处理搜索结果，封装成 SearchArticleVo 列表并返回
        List<SearchArticleVo> searchArticleVos = CollStreamUtil.toList(searchResponse.hits().hits(), hit -> {
            // 从搜索结果中获取文章信息
            SearchArticleVo searchArticleVo = hit.source();
            // 如果存在高亮字段，则设置高亮标题
            assert searchArticleVo != null;
            if (CollUtil.isNotEmpty(hit.highlight())) {
                String hTitle = StrUtil.join("", hit.highlight().get("title"));
                searchArticleVo.setH_title(hTitle);
            } else {
                // 如果没有高亮字段，则使用原标题
                searchArticleVo.setH_title(searchArticleVo.getTitle());
            }
            return searchArticleVo;
        });

        // 返回封装好的结果
        return ResponseResult.okResult(searchArticleVos);
    }



    // 私有方法，用于构建基本的查询条件
    private Query buildBasicQuery(UserSearchDto dto) {
        // 创建布尔查询构建器
        BoolQuery.Builder boolQueryBuilder = new BoolQuery.Builder();

        // 如果搜索词为空，则查询全部
        if (StrUtil.isEmpty(dto.getSearchWords())) {
            boolQueryBuilder.must(m -> m.matchAll(ma -> ma));
        } else {
            // 如果有搜索词，则构建匹配查询
            boolQueryBuilder.must(m -> m.match(mm -> mm.field("all").query(dto.getSearchWords())));
        }

        // 如果有最小热门时间，则添加时间过滤条件
        if (ObjectUtil.isNotEmpty(dto.getMinBehotTime())) {
            boolQueryBuilder.filter(f -> f.range(r -> r.field("publishTime").lt(JsonData.of(dto.getMinBehotTime().getTime()))));
        }

        // 构建并返回查询对象
        return new Query.Builder()
                .bool(boolQueryBuilder.build())
                .build();
    }




    @Override
    public ResponseResult<List<ApAssociateWords>> associativeWords(ApAssociateWordsDto dto) {
        String format = StrUtil.format("https://www.baidu.com/sugrec?prod=pc&wd={}", dto.getSearchWords());
        String body = HttpRequest.get(format).timeout(1000).execute().body();
        // 解析 JSON 字符串
        JSONObject jsonObject = JSONUtil.parseObj(body);
        // 从 JSON 对象中获取 'g' 数组
        JSONArray suggestions = jsonObject.getJSONArray("g");
        List<Suggestion> suggestionList = BeanUtil.copyToList(suggestions, Suggestion.class);

        List<ApAssociateWords> apAssociateWords = new ArrayList<>();
        for (Suggestion suggestion : suggestionList) {
            apAssociateWords.add(new ApAssociateWords(suggestion.getSa(),suggestion.getQ(), new Date()));
        }
        return ResponseResult.okResult(apAssociateWords);
    }
}